/*
	AIBID updater
	Copyright (C) 2012-2012 Metallhead <Metallhead@megabox.ru>

	HomePage:    http://code.google.com/p/aibid/

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QCheckBox>
#include <QFile>
#include <QDir>
#include <QTableWidget>

MainWindow::MainWindow(QString name, QWidget *parent) :
	QMainWindow(parent),
	ui(new Ui::MainWindow),
	programName(name)
{
	ui->setupUi(this);

	exit = false;

	//setting up tableWidget
	ui->filesList->setRowCount(0);
	ui->filesList->setColumnCount(2);
	QStringList headers;
	headers << tr("Files") << "";
	ui->filesList->setHorizontalHeaderLabels(headers);
	QHeaderView * header = ui->filesList->horizontalHeader();
	header->setResizeMode(QHeaderView::ResizeToContents);

	//setting buttons and labels
	ui->status->setText(tr("Requesting info from server..."));
	ui->update->setEnabled(false);


	//reading file files.lst
	QFile file(QDir::toNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/files.lst"));

	if(!file.exists())
	{
		//creating file if lost
		file.open(QIODevice::WriteOnly);
		file.close();
	}
	file.open(QIODevice::ReadOnly);
	QByteArray arr = file.readAll();
	file.close();

	//fill data
	fillFiles(arr, currFiles);

	//if file downloaded - update progress bar
	connect(this,SIGNAL(downloadedCountChanged(int)),this,SLOT(updateProgressBar(int)));

	//for files.lst text
	QTextCodec::setCodecForCStrings(QTextCodec::codecForName("windows-1251"));

	//if updater was launched from another program
	launchAfterUpdate = programName != "";

	//download files.lst
	downloadFilesList();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::fillFiles(QByteArray arr, QVector<fileWithVersion> & array)
{
	QString str = QString(arr);
	QStringList filesAndVersions = str.split("\r\n");
	foreach(QString file, filesAndVersions)
	{
		if(file.split(" ").count()!=4)
			continue;
		QString fileName = file.split(" ").at(1);
		QString fileType = file.split(" ").at(0);
		bool ok = false;
		double fileVersion;
		uint fileHash;
		fileVersion = file.split(" ").at(2).toDouble(&ok);
		if(ok)
			fileHash = file.split(" ").at(3).toUInt(&ok);
		if(ok)
		{
			fileWithVersion fWV;
			fWV.type = fileType;
			fWV.name = fileName;
			fWV.version = fileVersion;
			fWV.hash = fileHash;
			array.append(fWV);
		}
	}
}

void MainWindow::on_update_clicked()
{
	//if everything is ok we will say "goodbye"
	if(exit)
	{
		//if our updater was launched from aibid we need to launch aibid again
		if(launchAfterUpdate)
		{
			QFile file(programName);
			file.setPermissions(QFile::ExeGroup | QFile::ReadGroup | QFile::ExeOwner |  QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOther | QFile::ReadOther);
			QProcess * process = new QProcess();
			process->start("./"+programName);
			process->waitForStarted();
		}
		this->close();
	}

	//otherwise we will null progressbar and process out update
	ui->update->setEnabled(false);
	ui->currentProgressBar->setValue(0);
	ui->status->setText(tr("Processing update..."));

	processUpdate();
}

void MainWindow::processUpdate()
{
	//launching "downloadFile" function for every file
	foreach(fileWithVersion file, filesToDownload)
	{
		downloadFile(file);
	}
}

void MainWindow::downloadFilesList()
{
	QNetworkAccessManager * manager = new QNetworkAccessManager(this);
	connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(filesListDownloaded(QNetworkReply*)));
	manager->get(QNetworkRequest(QUrl(downloadAddress + "files.lst")));
}

void MainWindow::filesListDownloaded(QNetworkReply *reply)
{
	//files.lst was downloaded
	QByteArray data = reply->readAll();
	fillFiles(data, files);
	//remember files.lst data
	filesLst = data;

	filesNeededToDownload = 0;
	filesDownloaded = 0;

	//if some files are not in server files.lst but in client files.lst we need to remove them (and folders)
	foreach(fileWithVersion currFile, currFiles)
	{
		bool contains = false;
		foreach(fileWithVersion file, files)
		{
			if(file == currFile)
			{
				contains = true;
				break;
			}
		}

		if(!contains)
		{
			QFile f(currFile.name);
			f.remove();
			QString folder = currFile.name;
			while(folder.count()!=0)
			{
				if(folder.at(folder.count()-1)=='/')
				{
					QDir dir(folder);
					if(!dir.entryList(QDir::Files).count())
						dir.rmdir(".");
					else
						break;
				}
				folder.resize(folder.count()-1);
			}
		}
	}

	foreach(fileWithVersion file, files)
	{
		bool contains = false;
		foreach(fileWithVersion currFile, currFiles)
		{
			if(file == currFile)
			{
				contains = true;
				break;
			}
		}
		//if files not in client files.lst but in server files.lst we need to download them
		if(!contains)
		{
			file.number = filesNeededToDownload;
			filesNeededToDownload++;
			filesToDownload.append(file);
		}
		else
		{
			foreach(fileWithVersion currFile, currFiles)
			{
				//for files with equal names
				if(currFile == file)
				{
					//and different versions
					if(currFile != file)
					{
						//we will also download them
						file.number = filesNeededToDownload;
						filesNeededToDownload++;
						filesToDownload.append(file);
					}
					break;
				}
			}
		}
	}

	//if there are files to download
	if(filesToDownload.count()!=0)
	{
		//setting row count
		ui->filesList->setRowCount(filesToDownload.count());
		//setting table for each download
		foreach(fileWithVersion file, filesToDownload)
		{
			QTableWidgetItem* item = new QTableWidgetItem(file.name.split("/").last());
			ui->filesList->setItem(file.number, 0, item);
			item = new QTableWidgetItem("");
			ui->filesList->setItem(file.number, 1, item);
		}
		ui->status->setText(tr("Ready to update. Press \"update\" to update."));
	}
	//if there were no files to download
	else
	{
		//we must check all hashcodes
		exit = checkAllFiles();
	}

	ui->update->setEnabled(true);
}

void MainWindow::downloadFile(fileWithVersion file)
{
	//folder var is folder name
	QString folder = file.name;
	while(folder.count()!=0 && folder.at(folder.count()-1)!='/')
		folder.resize(folder.count()-1);

	//making path for file
	QDir dir;
	if(folder!="")
	{
		if(file.type=="bin")
			dir.setPath(".");
		else
			dir.setPath(QDesktopServices::storageLocation(QDesktopServices::DataLocation));
		if(!dir.exists(folder))
			dir.mkpath(folder);
	}

	//sending data to download object
	downloader * download = new downloader(file.type,file.number, file.name, file.version);
	connect(download, SIGNAL(signal(QNetworkReply*,int,QString,QString)), this, SLOT(fileDownloaded(QNetworkReply*,int,QString,QString)));
	//launching download
	download->downloadFile();
}

void MainWindow::fileDownloaded(QNetworkReply *reply, int row, QString fileName,QString fileType)
{
	//saving file
	QByteArray data = reply->readAll();
	QString path;
	if(fileType=="data")
		path = QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QString(QDir::separator()) + fileName;
	else
		path = fileName;
	QFile file(path);
	file.open(QIODevice::WriteOnly);
	file.write(data);
	file.close();

	//increasing downloaded count and checking checkbox
	incDownloadedCount(row);

	//if all files were downloaded we need to check them
	if(filesDownloaded==filesNeededToDownload)
	{
		exit = checkAllFiles();
	}
}

void MainWindow::incDownloadedCount(int row)
{
	filesDownloaded++;
	emit downloadedCountChanged(row);
}

void MainWindow::updateProgressBar(int row)
{
	QTableWidgetItem* item = ui->filesList->item(row, 1);
	item->setCheckState(Qt::Checked);
	ui->currentProgressBar->setValue((filesDownloaded*100)/filesNeededToDownload);
}

bool MainWindow::checkAllFiles()
{
	//looking for error
	bool error = false;
	filesToDownload.clear();
	int fileNumber = 0;
	//we need to take hash code of every file
	foreach(fileWithVersion file, files)
	{
		QString path;
		if(file.type=="data")
			path = QDir::toNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QString(QDir::separator()) + file.name);
		else
			path = file.name;
		qDebug() << path;
		QFile f(path);
		f.open(QIODevice::ReadOnly);
		QByteArray arr = f.readAll();
		uint hash = qHash(arr);
		qDebug() << hash;
		//if hash codes are not equal...
		if(hash!=file.hash)
		{
			//we'll set file number
			file.number = fileNumber;
			fileNumber++;
			//and row count
			ui->filesList->setRowCount(fileNumber);
			//and append this file to the "files to download"
			filesToDownload.append(file);
			//look! error is true!
			error = true;
			//setting up table
			QTableWidgetItem* item = new QTableWidgetItem(file.name.split("/").last());
			ui->filesList->setItem(file.number, 0, item);
			item = new QTableWidgetItem("");
			ui->filesList->setItem(file.number, 1, item);
		}
	}
	if(!error)
	{
		//if there were no error we'll just write all server files.lst data to file
		QFile file(QDir::toNativeSeparators(QDesktopServices::storageLocation(QDesktopServices::DataLocation) + "/files.lst"));
		file.open(QIODevice::WriteOnly);
		file.write(filesLst);
		file.close();
		ui->update->setText(tr("Finish"));
		ui->status->setText(tr("Nothing to update."));
		ui->currentProgressBar->setValue(100);
	}
	else
	{
		//otherwise we'll set counters
		ui->status->setText(tr("Some files are corrupted."));
		filesNeededToDownload = filesToDownload.count();
		filesDownloaded = 0;
	}
	ui->update->setEnabled(true);
	return !error;
}

downloader::downloader(QString type, int num, QString name, double version)  :
	number(num),
	fileName(name),
	fileVersion(version),
	fileType(type)
{
	manager = new QNetworkAccessManager(this);
	connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(sendSignal(QNetworkReply*)));
}

void downloader::downloadFile()
{
	QString url = downloadAddress + QString::number(fileVersion) + "/" + fileName;
	manager->get(QNetworkRequest(QUrl(url)));
}

void downloader::sendSignal(QNetworkReply *reply)
{
	emit signal(reply, number, fileName, fileType);
}
